/** Z80: portable Z80 emulator *******************************/ /** **/ /** Z80.c **/ /** **/ /** This file contains implementation for Z80 CPU. It can **/ /** be used separately to emulate any Z80-based machine. **/ /** In this case you will need: Z80.c, Z80.h, PTable.h, **/ /** Codes.h, CodesED.h, CodesCB.h, CodesXX.h, CodesXCB.h. **/ /** Don't forget to write Patch(), M_RDMEM(), and M_WRMEM() **/ /** functions to accomodate emulated machine architecture. **/ /** **/ /** Copyright (C) Marat Fayzullin 1994,1995,1996 **/ /** You are not allowed to distribute this software **/ /** commercially. Please, notify me, if you make any **/ /** changes to this file. **/ /*************************************************************/ #include #include "Z80.h" #include "Tables.h" /*** Registers ***********************************************/ /*** Z80 registers and running flag. ***/ /*************************************************************/ static reg R; byte CPURunning; /*** Interrupts **********************************************/ /*** Interrupt-related variables. ***/ /*************************************************************/ #ifdef INTERRUPTS int IPeriod = 2000; /* Number of cmds between int. intrpts */ byte IntSync = 1; /* 1 to generate internal interrupts */ byte IFlag = 0; /* If IFlag==1, gen. int. and set to 0 */ int ICount; /* Variable used to count CPU cycles */ #endif /*** Trace and Trap ******************************************/ /*** Switches to turn tracing on and off in DEBUG mode. ***/ /*************************************************************/ #ifdef DEBUG byte Trace=0; /* Tracing is on if Trace==1 */ word Trap=0xFFFF; /* When PC==Trap, set Trace=1 */ #endif /*** TrapBadOps **********************************************/ /*** When 1, print warnings of illegal Z80 instructions. ***/ /*************************************************************/ byte TrapBadOps=0; #define S(Fl) R.AF.B.l|=Fl #define R(Fl) R.AF.B.l&=~(Fl) #define FLAGS(Rg,Fl) R.AF.B.l=Fl|ZSTable[Rg] #define M_RLC(Rg) \ R.AF.B.l=Rg>>7;Rg=(Rg<<1)|R.AF.B.l;R.AF.B.l|=PZSTable[Rg] #define M_RRC(Rg) \ R.AF.B.l=Rg&0x01;Rg=(Rg>>1)|(R.AF.B.l<<7);R.AF.B.l|=PZSTable[Rg] #define M_RL(Rg) \ if(Rg&0x80) \ { \ Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \ R.AF.B.l=PZSTable[Rg]|C_FLAG; \ } \ else \ { \ Rg=(Rg<<1)|(R.AF.B.l&C_FLAG); \ R.AF.B.l=PZSTable[Rg]; \ } #define M_RR(Rg) \ if(Rg&0x01) \ { \ Rg=(Rg>>1)|(R.AF.B.l<<7); \ R.AF.B.l=PZSTable[Rg]|C_FLAG; \ } \ else \ { \ Rg=(Rg>>1)|(R.AF.B.l<<7); \ R.AF.B.l=PZSTable[Rg]; \ } #define M_SLA(Rg) \ R.AF.B.l=Rg>>7;Rg<<=1;R.AF.B.l|=PZSTable[Rg] #define M_SRA(Rg) \ R.AF.B.l=Rg&C_FLAG;Rg=(Rg>>1)|(Rg&0x80);R.AF.B.l|=PZSTable[Rg] #define M_SLL(Rg) \ R.AF.B.l=Rg>>7;Rg=(Rg<<1)|0x01;R.AF.B.l|=PZSTable[Rg] #define M_SRL(Rg) \ R.AF.B.l=Rg&0x01;Rg>>=1;R.AF.B.l|=PZSTable[Rg] #define M_BIT(Bit,Rg) \ R.AF.B.l=(R.AF.B.l&~(N_FLAG|Z_FLAG))|H_FLAG|(Rg&(1<PC.W=0x0000;Regs->SP.W=0xF000; Regs->AF.W=Regs->BC.W=Regs->DE.W=Regs->HL.W=0x0000; Regs->AF1.W=Regs->BC1.W=Regs->DE1.W=Regs->HL1.W=0x0000; Regs->IX.W=Regs->IY.W=0x0000; Regs->I=0x00;Regs->IFF=0x00; } /*** Interpret Z80 code: **********************************/ /*** Registers have initial values from Regs. PC value ***/ /*** at which emulation stopped is returned by this ***/ /*** function. ***/ /**********************************************************/ word Z80(reg Regs) { register byte I; register pair J; R=Regs;CPURunning=1; #ifdef INTERRUPTS ICount=IPeriod; IFlag=0; #endif for(;;) { #ifdef DEBUG if(R.PC.W==Trap) Trace=1; /*** Turn tracing on if trapped ***/ if(Trace) Debug(&R); /*** Call single-step debugger ***/ #endif switch(M_RDMEM(R.PC.W++)) { #include "Codes.h" case PFX_CB: CodesCB();break; case PFX_ED: CodesED();break; case PFX_FD: CodesFD();break; case PFX_DD: CodesDD();break; case HALT: #ifdef INTERRUPTS if(R.IFF&0x01) { R.PC.W--;R.IFF|=0x80; } #else printf("CPU HALTed and stuck at PC=%hX\n",--R.PC.W); CPURunning=0; #endif break; default: if(TrapBadOps) printf ( "Unrecognized instruction: %X at PC=%hX\n", M_RDMEM(R.PC.W-1),R.PC.W-1 ); } #ifndef INTERRUPTS if(!CPURunning) break; #else if(!ICount--) { if(!CPURunning) break; ICount=IPeriod; if(IntSync) IFlag=1; } if(IFlag) { IFlag=0;J.W=Interrupt(); if(((J.W!=0xFFFF)&&(R.IFF&0x01))||(J.W==0x0066)) { /* Experimental V Shouldn't disable all interrupts? */ R.IFF=(R.IFF&0xBE)|((R.IFF&0x01)<<6); if(R.IFF&0x80) { R.PC.W++;R.IFF&=0x7F; } M_PUSH(PC); if(J.W==0x0066) R.PC.W=0x0066; else if(R.IFF&0x04) { J.W&=0xFE;J.B.h=R.I; R.PC.B.l=M_RDMEM(J.W++); R.PC.B.h=M_RDMEM(J.W); } else if(R.IFF&0x02) R.PC.W=0x0038; else R.PC.W=J.W; } } #endif } return(R.PC.W); }